﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using Istace.Framework.Mapping.Core.Exceptions;
using Istace.Framework.Mapping.Core.Interfaces;
using Istace.Framework.Mapping.Core.Internal;

namespace Istace.Framework.Mapping.Core
{
    /// <summary>
    /// Facade for the mapper service
    /// </summary>
    public static class Mapper
    {
        #region IMapperService MapperInstance
        private static IMapperService mapperInstance;
        private static IMapperService MapperInstance
        {
            get
            {
                return mapperInstance ?? (mapperInstance = MapperService.Instance);
            }
        }

        /// <summary>
        /// Sets the IMapperService instance.
        /// </summary>
        /// <param name="instance">The MapperInstance.</param>
        public static void SetInstance(IMapperService instance)
        {
            if (instance == null) throw new ArgumentNullException("instance");
            mapperInstance = instance;
        }
        #endregion

        #region IMappingFrameworkBootstrapper MapperInstance
        private static IMappingFrameworkBootstrapper bootstrapperInstance;
        private static IMappingFrameworkBootstrapper BootstrapperInstance
        {
            get { return bootstrapperInstance ?? (bootstrapperInstance = MappingFrameworkBootstrapper.Instance); }
        }

        /// <summary>
        /// Sets the IMappingFrameworkBootstrapper instance.
        /// </summary>
        /// <param name="instance">The instance.</param>
        /// <exception cref="System.ArgumentNullException">instance</exception>
        public static void SetInstance(IMappingFrameworkBootstrapper instance)
        {
            if (instance == null) throw new ArgumentNullException("instance");
            bootstrapperInstance = instance;   
        }
        #endregion

        /// <summary>
        /// Resets this instances linked with the mapper service.
        /// </summary>
        public static void Reset()
        {
            mapperInstance = null;
            bootstrapperInstance = null;
        }

        /// <summary>
        /// Initializes the mappers.
        /// </summary>
        public static void Initialize()
        {
            BootstrapperInstance.Initialize();
        }

        /// <summary>
        /// Initialize the mapping framework
        /// </summary>
        /// <param name="baseAssembly">The base assembly</param>
        /// <param name="inspectReference">If true, all referenced assemblies will be inspected</param>
        /// <param name="reBrowse">If true, even if the assembly is already browsed, we will rebrowse it</param>
        public static void Initialize(Assembly baseAssembly, bool inspectReference, bool reBrowse)
        {
            BootstrapperInstance.Initialize(baseAssembly, inspectReference, reBrowse);
        }

        /// <summary>
        /// Map the TSource source object to the TDestination type
        /// </summary>
        /// <typeparam name="TSource">TSource type</typeparam>
        /// <typeparam name="TDestination">TDestination type</typeparam>
        /// <param name="source">Object to convert of TSsource type</param>
        /// <returns> Converted object to TDestination type </returns>
        /// <exception cref="MapperNotFoundException">Thrown if source is null</exception>
        public static TDestination Map<TSource, TDestination>(TSource source)
        {
            return MapperInstance.Map<TSource, TDestination>(source);
        }

        /// <summary>
        /// Map the TSource type source object to the TDestination type
        /// </summary>
        /// <typeparam name="TDestination">TDestination type</typeparam>
        /// <param name="source">Object to convert of TSource type</param>
        /// <returns> Converted object to TDestination type</returns>
        /// <remarks>The source type is found by reflection.</remarks>
        /// <exception cref="System.ArgumentNullException">Thrown if source is null</exception>
        /// <exception cref="MapperNotFoundException">Throw if no mappers match the TSource to TDestination mapping.</exception>
        public static TDestination Map<TDestination>(object source)
        {
            return MapperInstance.Map<TDestination>(source);
        }

        /// <summary>
        /// Map the TSource type source list object to the TDestination type
        /// </summary>
        /// <typeparam name="TSource">TSource type</typeparam>
        /// <typeparam name="TDestination">TDestination type</typeparam>
        /// <param name="source">Object list to convert of TSource type to TDestination type</param>
        /// <returns> Converted list of object to TDestination type</returns>
        /// <exception cref="System.ArgumentNullException">Thrown if source is null</exception>
        /// <exception cref="MapperNotFoundException">Throw if no mappers match the TSource to TDestination mapping.</exception>
        public static IEnumerable<TDestination> MapList<TSource, TDestination>(IEnumerable<TSource> source)
        {
            return MapperInstance.MapList<TSource, TDestination>(source);
        }

        /// <summary>
        /// Map the source object list to the TDestination type
        /// </summary>
        /// <typeparam name="TDestination">TDestination type</typeparam>
        /// <param name="source">Object list to convert to TDestination type</param>
        /// <returns> Converted list of object to TDestination type </returns>
        /// <remarks>The source type is found by reflection.</remarks>
        /// <exception cref="System.ArgumentNullException">Thrown if source is null</exception>
        /// <exception cref="MapperNotFoundException">Throw if no mappers match the TSource to TDestination mapping.</exception>
        public static IEnumerable<TDestination> MapList<TDestination>(IEnumerable source)
        {
            return MapperInstance.MapList<TDestination>(source);
        }
    }
}
